package service

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"mime/multipart"
	"net"
	"net/http"
	netUrl "net/url"
	"strings"
	"time"
)

type FileItem struct {
	Key      string //image_content
	FileName string //test.jpg
	Content  []byte //[]byte
}

type HttpConfig struct {
	Host           string `toml:"host" json:"host"`
	Port           int    `toml:"port" json:"port"`
	ConnectTimeout int    `toml:"connectTimeout" json:"connectTimeout"`
	Timeout        int    `toml:"timeout" json:"timeout"`
}

// 实例化http服务
type HttpService struct {
	clients map[string]*http.Client
}

func NewHttpService(clientMap map[string]HttpConfig) *HttpService {
	clients := make(map[string]*http.Client)

	for k, v := range clientMap {
		clients[k] = &http.Client{
			Transport: &http.Transport{
				MaxIdleConnsPerHost: 1024,
				IdleConnTimeout:     90 * time.Second,
				Proxy:               http.ProxyFromEnvironment,
				Dial: (&net.Dialer{
					Timeout:   time.Duration(v.ConnectTimeout) * time.Millisecond,
					KeepAlive: 300 * time.Second,
				}).Dial,
			},
			Timeout: time.Duration(v.Timeout) * time.Millisecond,
		}
	}

	return &HttpService{
		clients: clients,
	}
}

func (srv *HttpService) Shutdown() {
}

func (srv *HttpService) request(ctx context.Context, name string, method string, url string, body string, headers map[string]string) ([]byte, error) {
	if _, ok := srv.clients[name]; !ok {
		errMsg := fmt.Sprintf("httpclient.notfound client:%s", name)
		return nil, errors.New(errMsg)
	}

	m := strings.ToUpper(method)
	var request *http.Request
	if m == "GET" {
		request, _ = http.NewRequestWithContext(ctx, method, url, strings.NewReader(body))
	} else {
		request, _ = http.NewRequestWithContext(ctx, method, url, strings.NewReader(body))
	}

	for k, v := range headers {
		request.Header.Set(k, v)
		//golang 设置host才能生效
		if strings.ToLower(k) == "host" {
			request.Host = v
		}
	}

	response, err := srv.clients[name].Do(request)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()

	if response.StatusCode >= 200 && response.StatusCode < 300 {
		b, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return nil, err
		}
		return b, nil
	} else {
		b, _ := ioutil.ReadAll(response.Body)
		return nil, fmt.Errorf("httpclient.response: %s, code:%d", string(b), int32(response.StatusCode))
	}
}

func (srv *HttpService) requestWithBytesReturnResp(ctx context.Context, name string, method string, url string, body []byte, headers map[string]string) ([]byte, *http.Response, error) {
	if _, ok := srv.clients[name]; !ok {
		errMsg := fmt.Sprintf("httpclient.notfound client:%s", name)
		return nil, nil, errors.New(errMsg)
	}

	m := strings.ToUpper(method)
	var request *http.Request
	if m == "GET" {
		request, _ = http.NewRequest(method, url, nil)
	} else {
		request, _ = http.NewRequest(method, url, bytes.NewReader(body))
	}

	for k, v := range headers {
		request.Header.Set(k, v)
		//golang 设置host才能生效
		if strings.ToLower(k) == "host" {
			request.Host = v
		}
	}

	response, err := srv.clients[name].Do(request)
	if err != nil {
		return nil, nil, err
	}
	defer response.Body.Close()

	if response.StatusCode >= 200 || response.StatusCode < 300 {
		b, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return nil, response, err
		}
		return b, response, nil
	} else {
		b, _ := ioutil.ReadAll(response.Body)
		return nil, response, fmt.Errorf("httpclient.response: %s, code:%d", string(b), int32(response.StatusCode))
	}
}

func (srv *HttpService) requestWithBytesAndRespHeaders(ctx context.Context, name string, method string, url string, body []byte, headers map[string]string) ([]byte, map[string]string, error) {
	if _, ok := srv.clients[name]; !ok {
		errMsg := fmt.Sprintf("httpclient.notfound client:%s", name)
		return nil, nil, errors.New(errMsg)
	}

	m := strings.ToUpper(method)
	var request *http.Request
	if m == "GET" {
		request, _ = http.NewRequest(method, url, nil)
	} else {
		request, _ = http.NewRequest(method, url, bytes.NewReader(body))
	}

	for k, v := range headers {
		request.Header.Set(k, v)
		//golang 设置host才能生效
		if strings.ToLower(k) == "host" {
			request.Host = v
		}
	}

	response, err := srv.clients[name].Do(request)
	if err != nil {
		return nil, nil, err
	}
	defer response.Body.Close()
	respHeaders := make(map[string]string)
	for k, h := range response.Header {
		if len(h) > 0 {
			respHeaders[k] = h[0]
		}
	}

	if response.StatusCode >= 200 || response.StatusCode < 300 {
		b, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return nil, respHeaders, err
		}
		return b, respHeaders, nil
	} else {
		b, _ := ioutil.ReadAll(response.Body)
		return nil, respHeaders, fmt.Errorf("httpclient.response: %s, code:%d", string(b), int32(response.StatusCode))
	}
}

// 模拟发送GET请求
func (srv *HttpService) Get(ctx context.Context, name string, url string, headers map[string]string) ([]byte, error) {
	return srv.request(ctx, name, "GET", url, "", headers)
}

func (srv *HttpService) GetWithBytes(ctx context.Context, name string, url string, headers map[string]string) ([]byte, map[string]string, error) {
	return srv.requestWithBytesAndRespHeaders(ctx, name, "GET", url, nil, headers)
}

func (srv *HttpService) GetWithBytesReturnResponse(ctx context.Context, name string, url string, headers map[string]string) ([]byte, *http.Response, error) {
	return srv.requestWithBytesReturnResp(ctx, name, "GET", url, nil, headers)
}

func (srv *HttpService) GetWithJson(ctx context.Context, name string, url string, headers map[string]string, body string) ([]byte, error) {
	return srv.request(ctx, name, "GET", url, body, headers)
}

// 模拟发送GET请求
func (srv *HttpService) GetWithParams(ctx context.Context, name string, url string, headers map[string]string, params map[string]string) ([]byte, error) {
	url = strings.TrimSuffix(url, "?")
	url = strings.TrimSuffix(url, "&")

	p := ""
	if params != nil {
		for k, v := range params {
			p = p + (k + "=" + netUrl.QueryEscape(v) + "&")
		}
		p = strings.TrimSuffix(p, "&")
	}

	if strings.Contains(url, "?") {
		if p != "" {
			url = url + "&" + p
		}
	} else {
		if p != "" {
			url = url + "?" + p
		}
	}

	return srv.Get(ctx, name, url, headers)
}

// 模拟发送POST请求
func (srv *HttpService) Post(ctx context.Context, name string, url string, headers map[string]string, body string) ([]byte, error) {
	return srv.request(ctx, name, "POST", url, body, headers)
}

func (srv *HttpService) PostWithBytes(ctx context.Context, name string, url string, headers map[string]string, body []byte) ([]byte, map[string]string, error) {
	return srv.requestWithBytesAndRespHeaders(ctx, name, "POST", url, body, headers)
}

// 模拟发送POST请求
func (srv *HttpService) PostWithJson(ctx context.Context, name string, url string, headers map[string]string, params map[string]string) ([]byte, error) {
	body := "{}"
	if params != nil {
		bs, err := json.Marshal(params)
		if err != nil {
			return nil, err
		}

		body = string(bs)
	}

	if headers == nil {
		headers = make(map[string]string)
	}

	headers["Content-Type"] = "application/json"

	return srv.Post(ctx, name, url, headers, body)
}

// 模拟发送POST请求
func (srv *HttpService) PostWithFormData(ctx context.Context, name string, url string, headers map[string]string, params map[string]string) ([]byte, error) {
	body := ""
	if params != nil {
		for k, v := range params {
			body = body + (k + "=" + v + "&")
		}
		body = strings.TrimSuffix(body, "&")
	}

	if headers == nil {
		headers = make(map[string]string)
	}

	headers["Content-Type"] = "application/x-www-form-urlencoded"

	return srv.Post(ctx, name, url, headers, body)
}

func (srv *HttpService) PostWithMultipart(ctx context.Context, name string, url string, headers map[string]string, params map[string]string, paramFile *FileItem) ([]byte, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	for key, val := range params {
		_ = writer.WriteField(key, val)
	}

	if paramFile != nil {
		fileWriter, err := writer.CreateFormFile(paramFile.Key, paramFile.FileName)
		if err != nil {
			return nil, err
		}
		fileWriter.Write(paramFile.Content)
	}

	writer.Close()

	if headers == nil {
		headers = make(map[string]string)
	}

	headers["Content-Type"] = writer.FormDataContentType()

	return srv.request(ctx, name, "POST", url, body.String(), headers)
}

// 模拟发送PUT请求
func (srv *HttpService) Put(ctx context.Context, name string, url string, body string, headers map[string]string) ([]byte, error) {
	return srv.request(ctx, name, "PUT", url, body, headers)
}

// 模拟发送DELETE请求
func (srv *HttpService) Delete(ctx context.Context, name string, url string, body string, headers map[string]string) ([]byte, error) {
	return srv.request(ctx, name, "DELETE", url, body, headers)
}

// // ================================================================
// // 修改request接口定义
// // ================================================================

// func (srv *HttpService) requestV2(ctx context.Context, name string, method string, url string, headers map[string]string, params map[string]interface{}, body io.Reader) ([]byte, error) {
// 	if _, ok := srv.clients[name]; !ok {
// 		return nil, errors.BadRequest("httpclient.notfound", "unknown http client: %s", name)
// 	}

// 	m := strings.ToUpper(method)
// 	var request *http.Request
// 	if m == "GET" {
// 		request, _ = http.NewRequest(method, url, nil)
// 	} else {
// 		request, _ = http.NewRequest(method, url, body)
// 	}

// 	for k, v := range headers {
// 		request.Header.Set(k, v)
// 	}

// 	if srv.sw != nil {
// 		b, _ := ioutil.ReadAll(body)
// 		return middleware.CreateHttpSpan(srv.sw, ctx, method, request.Host, srv.clients[name], request, string(b))
// 	}

// 	response, err := srv.clients[name].Do(request)
// 	if err != nil {
// 		return nil, err
// 	}
// 	defer response.Body.Close()

// 	if response.StatusCode >= 200 || response.StatusCode < 300 {
// 		b, err := ioutil.ReadAll(response.Body)
// 		if err != nil {
// 			return nil, err
// 		}
// 		return b, nil
// 	} else {
// 		b, _ := ioutil.ReadAll(response.Body)
// 		return nil, errors.New("httpclient.response", string(b), int32(response.StatusCode))
// 	}
// }

// //模拟发送POST请求
// func (srv *HttpService) PostForm(ctx context.Context, name string, url string, headers map[string]string, params map[string]string, body url.Values) ([]byte, error) {
// 	// return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())
// 	// req, err := NewRequest("POST", url, body)
// 	// if err != nil {
// 	// 	return nil, err
// 	// }
// 	// req.Header.Set("Content-Type", contentType)
// 	// return c.Do(req))
// 	if headers == nil {
// 		headers = make(map[string]string)
// 	}

// 	headers["Content-Type"] = "application/x-www-form-urlencoded"

// 	// "application/x-www-form-urlencoded", strings.NewReader(data.Encode())

// 	return srv.requestV2(ctx, name, "POST", url, headers, params, strings.NewReader(body.Encode()))
// }
